package src.ee.ioc.smaug.world.beings;

import src.ee.ioc.smaug.fsm.*;

import src.ee.ioc.smaug.world.*;

/**
 * Idividual representing Mealy machine with fixed number of states
 * 
 * @author Margarita Spichakova
 * 
 */
public class MealySMFixedNumofStates extends Individual {
    int sizeAlpha = Enviroment.inAlphabet.getSizeOfAlphabet();

    int bitsPerTransition = countBitsPerTransition();

    int bitsPerState = countBitsPerState();

    int bitPerSection = sizeAlpha * (bitsPerState + bitsPerTransition);

    /**
     * Constructor
     */
    public MealySMFixedNumofStates() {
        fenotype = new MealyMachine();
        define();
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#generateInBinaryForm()
     */
    public void generateInBinaryForm() {

        genotype = new boolean[bitPerSection * Enviroment.numberOfStates];

        for (int i = 0; i < bitPerSection * Enviroment.numberOfStates; i++) {
            genotype[i] = Coin.flipCoin();
        }
        while (isDead()) {
            for (int i = 0; i < bitPerSection * Enviroment.numberOfStates; i++) {
                genotype[i] = Coin.flipCoin();
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#generateInDecinalForm()
     */
    public void generateInDecinalForm() {
        generateInBinaryForm();
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#define()
     */
    public void define() {
        genotype = new boolean[bitPerSection * Enviroment.numberOfStates];
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#isDead()
     */
    public boolean isDead() {
        boolean dead = false;
        decodeBinToDec();

        for (int i = 0; i < Enviroment.numberOfStates
                * Enviroment.inAlphabet.getSizeOfAlphabet(); i++) {
            if (genotypeDec[2 * i] >= Enviroment.outAlphabet
                    .getSizeOfAlphabet()) {
                dead = true;
            }
            if (genotypeDec[2 * i + 1] >= Enviroment.numberOfStates) {
                dead = true;
            }
        }

        return dead;
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#decode()
     */
    public void decode() {
        decodeBinToDec();
        decodeDecToFSM();
        this.genotypeDec = null;

    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#decodeBinToDec()
     */
    public void decodeBinToDec() {
        genotypeDec = new int[Enviroment.numberOfStates * 2
                * Enviroment.inAlphabet.getSizeOfAlphabet()];

        for (int i = 0; i < Enviroment.numberOfStates * sizeAlpha; i++) {
            String s = "";

            for (int j = 0; j < bitsPerTransition; j++) {
                s += Coin.booleanToInteger(genotype[i
                        * (bitsPerTransition + bitsPerState) + j]);

            }

            genotypeDec[2 * i] = Integer.parseInt(s, 2);
            s = "";

            for (int j = 0; j < bitsPerState; j++) {
                s += Coin.booleanToInteger(genotype[i
                        * (bitsPerTransition + bitsPerState)
                        + bitsPerTransition + j]);

            }

            genotypeDec[2 * i + 1] = Integer.parseInt(s, 2);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#decodeDecToFSM()
     */
    public void decodeDecToFSM() {
        fenotype = new MealyMachine();
        fenotype.setInputAlphabet(Enviroment.inAlphabet);
        fenotype.setOutputAlphabet(Enviroment.outAlphabet);
        int memPerState = 2 * Enviroment.inAlphabet.getSizeOfAlphabet();
        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            State q = new State(i, '-', false, false);
            if (i == 0) {
                q.setInitialState(true);
            }
            fenotype.addState(q);
        }
        fenotype.setBounds();

        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            State fromState = this.fenotype.getState(i);
            for (int j = 0; j < Enviroment.inAlphabet.getSizeOfAlphabet(); j++) {

                char inChar = this.fenotype.getInputAlphabet()
                        .returnSymbolAtPosition(j);

                char outChar = this.fenotype.getOutputAlphabet()
                        .returnSymbolAtPosition(
                                genotypeDec[i * memPerState + j * 2]);

                State toState = this.fenotype.getState(genotypeDec[i
                        * memPerState + j * 2 + 1]);

                Transition qToQRandChar = new Transition(fromState, toState,
                        inChar, outChar);
                fenotype.addTransition(qToQRandChar);
            }
        }

    }

    /**
     * @return number of states required to store state
     */
    public int countBitsPerState() {
        int bitsPerState = (int) Math.round(Math.log(Enviroment.numberOfStates)
                / Math.log(2));

        if (Enviroment.numberOfStates == 1) {
            return 1;
        }
        if (Enviroment.numberOfStates <= Math.pow(2, bitsPerState)) {
            return bitsPerState;
        } else
            return bitsPerState + 1;

    }

    /**
     * @return number of states required to store transition
     */
    public int countBitsPerTransition() {
        int sizeBeta = Enviroment.outAlphabet.getSizeOfAlphabet();
        int bitsPerOutput = (int) Math.round(Math.log(sizeBeta) / Math.log(2));
        if (sizeBeta <= Math.pow(2, bitsPerOutput)) {
            return bitsPerOutput;
        } else
            return bitsPerOutput + 1;

    }

}
